package cc.wanforme.fsync.server.task;

import cc.wanforme.ServerApplication;
import cc.wanforme.fsync.config.restUpload.SimpleProgressTracker;
import cc.wanforme.fsync.config.restUpload.UploadProgressCenter;
import cc.wanforme.fsync.entity.po.SyncLog;
import cc.wanforme.fsync.entity.ws.ProgressVo;
import cc.wanforme.fsync.entity.ws.WsReq;
import cc.wanforme.fsync.entity.ws.WsRes;
import cc.wanforme.fsync.server.service.ISyncLogService;
import cc.wanforme.st.server.util.SimpleJackson;
import cc.wanforme.st.server.vo.ResMessage;
import cn.dev33.satoken.stp.StpUtil;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;

/** 获取任务状态<br/>
 * websocket 处理器是多例 */
@ServerEndpoint("/tasksock/{userId}")
@Component
public class TaskWebsocket {

    private boolean verified;
    private Session session;

    /**
     * 建立WebSocket连接
     *
     * @param session
     * @param userId 用户ID
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId") Long userId) {
        //System.out.println("onopen, userId: " + userId + ", addr=" + toString());
        this.session = session;
        getSessions().add(userId, session);
    }

    /**
     * 发生错误
     *
     * @param throwable e
     */
    @OnError
    public void onError(Throwable throwable, @PathParam(value = "userId") Long userId)
            throws Throwable {
        throw  throwable;
    }

    /**
     * 连接关闭
     */
    @OnClose
    public void onClose(@PathParam(value = "userId") Long userId) throws IOException {
        // System.out.println("onclose, userId: " + userId);
        getSessions().remove(userId, session);
    }

    /**
     * 接收客户端消息
     *
     * @param message 接收的消息
     */
    @OnMessage
    public void onMessage(String message, @PathParam(value = "userId") Long userId,
                          Session session)
            throws IOException {
        if(!checkLogin(userId, session)) {
            sendMessage(session, ResMessage.relogin("未登录"));
            return;
        }

        WsReq req = SimpleJackson.toObject(message, WsReq.class);
        if(!StringUtils.hasText(req.getType())) {
            return;
        }

        // 先请求 LOGIN ，验证登录
        // 再请求 TASKS ，获取所有正在发送的文件
        String data = req.getData();
        boolean sendMsg =  switch (req.getType()) {
            case WsReq.LOGIN -> handleLogin(userId, data, session);
            case WsReq.TASKS -> {
                if(!checkVerified(session)) {
                    yield false;
                }
                yield handleTask(userId, data, session);
            }
            default -> false;
        };

        // 是否发送执行成功的消息
        if(sendMsg) {
            sendMessage(session, ResMessage.success());
        }
    }

    /** 检查登录情况 */
    private boolean checkLogin(Long userId, Session session)
            throws IOException {
        if(!StpUtil.isLogin(userId)) {
            sendMessage(session, ResMessage.relogin("未登录"));
            return false;
        }
        return true;
    }
    private boolean checkVerified(Session session) throws IOException {
        if(!verified) {
            sendMessage(session, ResMessage.relogin("未登录"));
        }
        return verified;
    }

    /** 处理登录，检查 token */
    public boolean handleLogin(Long userId, String token, Session session) throws IOException {
        List<String> tokens = StpUtil.getTokenValueListByLoginId(userId);
        if(tokens == null || !tokens.contains(token)) {
            sendMessage(session, ResMessage.fail("登录无效"));
            return false;
        }

        // 不要保持唯一
        sendMessage(session, WsRes.New(WsRes.LOGIN, Boolean.TRUE));
        verified = true;
        return false;
    }

    /** 获取所有的任务进度 */
    private boolean handleTask(Long userId, String data, Session session)
            throws IOException {
        Map<Long, SimpleProgressTracker> all = UploadProgressCenter.getAll();
//        Map<Long, SimpleProgressTracker> all = mockData();
        // 转换实体
        Map<Long, ProgressVo> allRes = convertToVo(all);
        // 没有则返回空对象
        sendMessage(session, WsRes.New(WsRes.TASKS, !allRes.isEmpty() ? allRes : null));
        return false;
    }

    /** 测试数据 */
    private static int progress = 0;
    @SuppressWarnings("unused")
    private Map<Long, SimpleProgressTracker> mockData() {
        Map<Long, SimpleProgressTracker> mockData = new HashMap<>();
        SimpleProgressTracker t = new SimpleProgressTracker(518729147L);
        mockData.put(275L, t);
        long sizeSend = progress * t.getSizeTotal() / 100;
        t.trackProgress(progress, sizeSend);

        progress = (progress + 10) % 110;
        return mockData;
    }

    private Map<Long, ProgressVo> convertToVo(Map<Long, SimpleProgressTracker> all) {
        Map<Long, ProgressVo> allRes = new HashMap<>(all.size());
        Iterator<Map.Entry<Long, SimpleProgressTracker>> iterator = all.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Long, SimpleProgressTracker> next = iterator.next();
            Long key = next.getKey();
            ProgressVo value = ProgressVo.from(next.getValue());
            SyncLog syncLog = getSyncLogService().getById(key);
            value.setLog(syncLog);

            allRes.put(key, value);
        }
        return allRes;
    }

    public static void sendMessage(Session session, WsRes<?> msg) throws IOException {
        sendMessage(session, ResMessage.success(msg));
    }
    private static void sendMessage(Session session, ResMessage<?> msg) throws IOException {
        String msgStr = SimpleJackson.toJsonString(msg);
        sendMessage(session, msgStr);
    }
    private static void sendMessage(Session session, String msg) throws IOException {
        session.getBasicRemote().sendText(msg);
    }

    public ISyncLogService getSyncLogService() {
        return ServerApplication.getAppContext().getBean(ISyncLogService.class);
    }
    public TaskWebsocketSessions getSessions() {
        return ServerApplication.getAppContext().getBean(TaskWebsocketSessions.class);
    }

}
